import SparkMD5 from 'spark-md5'
import axios from 'axios'

// 分片大小，默认2MB
const CHUNK_SIZE = 2 * 1024 * 1024

// API基础URL
const API_BASE_URL = '/upload'

/**
 * 计算文件MD5
 * @param {File} file 文件对象
 * @param {Function} onProgress 进度回调
 * @returns {Promise<string>} MD5值
 */
export function calculateFileMD5(file, onProgress) {
  return new Promise((resolve, reject) => {
    const spark = new SparkMD5.ArrayBuffer()
    const fileReader = new FileReader()
    const chunks = Math.ceil(file.size / CHUNK_SIZE)
    let currentChunk = 0

    fileReader.onload = (e) => {
      spark.append(e.target.result)
      currentChunk++

      if (onProgress) {
        onProgress(Math.round((currentChunk / chunks) * 100))
      }

      if (currentChunk < chunks) {
        loadNext()
      } else {
        resolve(spark.end())
      }
    }

    fileReader.onerror = () => {
      reject(new Error('文件读取失败'))
    }

    function loadNext() {
      const start = currentChunk * CHUNK_SIZE
      const end = Math.min(start + CHUNK_SIZE, file.size)
      fileReader.readAsArrayBuffer(file.slice(start, end))
    }

    loadNext()
  })
}

/**
 * 创建文件分片
 * @param {File} file 文件对象
 * @returns {Array} 分片数组
 */
export function createFileChunks(file) {
  const chunks = []
  const totalChunks = Math.ceil(file.size / CHUNK_SIZE)

  for (let i = 0; i < totalChunks; i++) {
    const start = i * CHUNK_SIZE
    const end = Math.min(start + CHUNK_SIZE, file.size)
    chunks.push({
      index: i,
      chunk: file.slice(start, end),
      size: end - start
    })
  }

  return chunks
}

/**
 * 检查文件是否存在（秒传检查）
 * @param {string} md5 文件MD5
 * @param {string} fileName 文件名
 * @param {number} fileSize 文件大小
 * @returns {Promise} 检查结果
 */
export async function checkFileExists(md5, fileName, fileSize) {
  try {
    const response = await axios.post(`${API_BASE_URL}/check`, {
      md5,
      fileName,
      fileSize
    })
    return response.data
  } catch (error) {
    throw new Error('检查文件失败: ' + error.message)
  }
}

/**
 * 批量检查文件是否存在（多文件秒传检查）
 * @param {Array} files 文件列表 [{md5, fileName, fileSize}, ...]
 * @returns {Promise} 批量检查结果
 */
export async function batchCheckFileExists(files) {
  try {
    const response = await axios.post(`${API_BASE_URL}/batch-check`, {
      files
    })
    return response.data
  } catch (error) {
    throw new Error('批量检查文件失败: ' + error.message)
  }
}

/**
 * 获取已上传的分片列表
 * @param {string} md5 文件MD5
 * @returns {Promise<Array>} 已上传分片索引数组
 */
export async function getUploadedChunks(md5) {
  try {
    const response = await axios.get(`${API_BASE_URL}/chunks/${md5}`)
    return response.data.data || []
  } catch (error) {
    console.error('获取已上传分片失败:', error)
    return []
  }
}

/**
 * 上传单个分片
 * @param {string} md5 文件MD5
 * @param {number} chunkIndex 分片索引
 * @param {number} totalChunks 总分片数
 * @param {Blob} chunk 分片数据
 * @param {Function} onProgress 进度回调（可选）
 * @returns {Promise} 上传结果
 */
export async function uploadChunk(md5, chunkIndex, totalChunks, chunk, onProgress = null) {
  const formData = new FormData()
  formData.append('md5', md5)
  formData.append('chunkIndex', chunkIndex)
  formData.append('totalChunks', totalChunks)
  formData.append('file', chunk)

  try {
    const response = await axios.post(`${API_BASE_URL}/chunk`, formData, {
      headers: {
        'Content-Type': 'multipart/form-data'
      },
      onUploadProgress: (progressEvent) => {
        if (onProgress) {
          const percent = Math.round(
            (progressEvent.loaded * 100) / progressEvent.total
          )
          onProgress(percent)
        }
      }
    })
    return response.data
  } catch (error) {
    throw new Error(`分片${chunkIndex}上传失败: ${error.message}`)
  }
}

/**
 * 合并文件分片
 * @param {string} md5 文件MD5
 * @param {string} fileName 文件名
 * @param {number} totalChunks 总分片数
 * @param {number} fileSize 文件大小
 * @returns {Promise} 合并结果
 */
export async function mergeChunks(md5, fileName, totalChunks, fileSize) {
  try {
    const response = await axios.post(`${API_BASE_URL}/merge`, {
      md5,
      fileName,
      totalChunks,
      fileSize
    }, {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      },
      transformRequest: [function (data) {
        let ret = ''
        for (let it in data) {
          ret += encodeURIComponent(it) + '=' + encodeURIComponent(data[it]) + '&'
        }
        return ret.substring(0, ret.length - 1)
      }]
    })
    return response.data
  } catch (error) {
    throw new Error('文件合并失败: ' + error.message)
  }
}

/**
 * 保存上传状态到本地存储
 * @param {string} fileId 文件ID
 * @param {Object} uploadState 上传状态
 */
export function saveUploadState(fileId, uploadState) {
  const key = `upload_state_${fileId}`
  localStorage.setItem(key, JSON.stringify(uploadState))
}

/**
 * 从本地存储获取上传状态
 * @param {string} fileId 文件ID
 * @returns {Object|null} 上传状态
 */
export function getUploadState(fileId) {
  const key = `upload_state_${fileId}`
  const state = localStorage.getItem(key)
  return state ? JSON.parse(state) : null
}

/**
 * 清除本地存储的上传状态
 * @param {string} fileId 文件ID
 */
export function clearUploadState(fileId) {
  const key = `upload_state_${fileId}`
  localStorage.removeItem(key)
}

/**
 * 并发控制类
 */
export class ConcurrencyController {
  constructor(limit = 3) {
    this.limit = limit
    this.running = 0
    this.queue = []
  }

  async add(task) {
    return new Promise((resolve, reject) => {
      this.queue.push({
        task,
        resolve,
        reject
      })
      this.process()
    })
  }

  async process() {
    if (this.running >= this.limit || this.queue.length === 0) {
      return
    }

    this.running++
    const { task, resolve, reject } = this.queue.shift()

    try {
      const result = await task()
      resolve(result)
    } catch (error) {
      reject(error)
    } finally {
      this.running--
      this.process()
    }
  }
}

/**
 * 多文件上传管理器
 */
export class MultiFileUploadManager {
  constructor(options = {}) {
    this.files = new Map() // 文件列表
    this.concurrencyLimit = options.concurrencyLimit || 2 // 并发文件数限制
    this.chunkConcurrency = options.chunkConcurrency || 3 // 每个文件的分片并发数
    this.onProgress = options.onProgress || (() => {})
    this.onFileProgress = options.onFileProgress || (() => {})
    this.onComplete = options.onComplete || (() => {})
    this.onError = options.onError || (() => {})
    
    this.activeTasks = 0
    this.taskQueue = []
  }

  /**
   * 添加文件到上传队列
   * @param {File} file 文件对象
   * @returns {string} 文件ID
   */
  addFile(file) {
    const fileId = this.generateFileId(file)
    const fileData = {
      id: fileId,
      file: file,
      name: file.name,
      size: file.size,
      status: 'waiting', // waiting, calculating, uploading, paused, completed, failed
      progress: 0,
      md5: null,
      chunks: [],
      uploadedChunks: [],
      error: null,
      startTime: null,
      uploadedBytes: 0
    }
    
    this.files.set(fileId, fileData)
    return fileId
  }

  /**
   * 生成文件ID
   * @param {File} file 文件对象
   * @returns {string} 文件ID
   */
  generateFileId(file) {
    return `${file.name}_${file.size}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
  }

  /**
   * 移除文件
   * @param {string} fileId 文件ID
   */
  removeFile(fileId) {
    const fileData = this.files.get(fileId)
    if (fileData) {
      // 清理本地存储
      clearUploadState(fileId)
      this.files.delete(fileId)
    }
  }

  /**
   * 开始上传所有文件
   */
  async startAllUploads() {
    const waitingFiles = Array.from(this.files.values()).filter(f => f.status === 'waiting')
    
    for (const fileData of waitingFiles) {
      this.enqueueFileUpload(fileData.id)
    }
  }

  /**
   * 暂停所有上传
   */
  pauseAllUploads() {
    this.files.forEach(fileData => {
      if (fileData.status === 'uploading') {
        fileData.status = 'paused'
      }
    })
  }

  /**
   * 恢复所有上传
   */
  resumeAllUploads() {
    const pausedFiles = Array.from(this.files.values()).filter(f => f.status === 'paused')
    
    for (const fileData of pausedFiles) {
      this.enqueueFileUpload(fileData.id)
    }
  }

  /**
   * 将文件上传任务加入队列
   * @param {string} fileId 文件ID
   */
  enqueueFileUpload(fileId) {
    if (this.activeTasks < this.concurrencyLimit) {
      this.startFileUpload(fileId)
    } else {
      this.taskQueue.push(fileId)
    }
  }

  /**
   * 开始单个文件上传
   * @param {string} fileId 文件ID
   */
  async startFileUpload(fileId) {
    const fileData = this.files.get(fileId)
    if (!fileData || fileData.status !== 'waiting' && fileData.status !== 'paused') {
      return
    }

    this.activeTasks++
    fileData.status = 'calculating'
    fileData.startTime = Date.now()

    try {
      // 计算MD5
      if (!fileData.md5) {
        fileData.md5 = await calculateFileMD5(fileData.file, (progress) => {
          this.onFileProgress(fileId, { type: 'md5', progress })
        })
      }

      // 检查秒传
      const checkResult = await checkFileExists(fileData.md5, fileData.name, fileData.size)
      
      if (checkResult.data.exists) {
        fileData.status = 'completed'
        fileData.progress = 100
        
        // 立即通知状态更新
        this.onFileProgress(fileId, { 
          type: 'completed', 
          message: '秒传成功',
          status: 'completed',
          progress: 100
        })
        
        this.onTaskComplete(fileId)
        return
      }

      // 创建分片
      if (fileData.chunks.length === 0) {
        fileData.chunks = createFileChunks(fileData.file)
      }

      // 获取已上传分片
      if (checkResult.data.uploadedChunks) {
        fileData.uploadedChunks = checkResult.data.uploadedChunks
        fileData.uploadedBytes = fileData.uploadedChunks.length * CHUNK_SIZE
        fileData.progress = Math.round((fileData.uploadedChunks.length / fileData.chunks.length) * 100)
      }

      fileData.status = 'uploading'

      // 上传分片
      await this.uploadFileChunks(fileId)

    } catch (error) {
      fileData.status = 'failed'
      fileData.error = error.message
      this.onError(fileId, error)
      this.onTaskComplete(fileId)
    }
  }

  /**
   * 上传文件分片
   * @param {string} fileId 文件ID
   */
  async uploadFileChunks(fileId) {
    const fileData = this.files.get(fileId)
    const concurrencyController = new ConcurrencyController(this.chunkConcurrency)

    // 找出未上传的分片
    const pendingChunks = fileData.chunks.filter(chunk => 
      !fileData.uploadedChunks.includes(chunk.index)
    )

    // 上传分片
    const uploadPromises = pendingChunks.map(chunkData => 
      concurrencyController.add(async () => {
        if (fileData.status !== 'uploading') {
          throw new Error('Upload cancelled')
        }

        await uploadChunk(
          fileData.md5,
          chunkData.index,
          fileData.chunks.length,
          chunkData.chunk
        )
        
        // 更新进度
        fileData.uploadedChunks.push(chunkData.index)
        fileData.uploadedBytes += chunkData.size
        fileData.progress = Math.round((fileData.uploadedChunks.length / fileData.chunks.length) * 100)
        
        // 保存状态（不保存chunks，因为Blob对象无法序列化）
        saveUploadState(fileId, {
          md5: fileData.md5,
          uploadedChunks: fileData.uploadedChunks,
          totalChunks: fileData.chunks.length,
          fileSize: fileData.size,
          fileName: fileData.name
        })
        
                 this.onFileProgress(fileId, { 
           type: 'upload', 
           progress: fileData.progress,
           uploadedChunks: fileData.uploadedChunks.length,
           totalChunks: fileData.chunks.length,
           status: fileData.status
         })
      })
    )

    await Promise.all(uploadPromises)

    if (fileData.status !== 'uploading') {
      return
    }

    // 合并文件
    const result = await mergeChunks(
      fileData.md5,
      fileData.name,
      fileData.chunks.length,
      fileData.size
    )

    if (result.success) {
      fileData.status = 'completed'
      fileData.progress = 100
      clearUploadState(fileId)
      
      // 立即通知状态更新
      this.onFileProgress(fileId, { 
        type: 'completed', 
        message: '上传成功',
        status: 'completed',
        progress: 100
      })
      
      this.onTaskComplete(fileId)
    } else {
      throw new Error(result.message || '文件合并失败')
    }
  }

  /**
   * 任务完成处理
   * @param {string} fileId 文件ID
   */
  onTaskComplete(fileId) {
    this.activeTasks--
    
    // 处理队列中的下一个任务
    if (this.taskQueue.length > 0) {
      const nextFileId = this.taskQueue.shift()
      this.startFileUpload(nextFileId)
    }

    // 检查是否所有任务完成
    const allFiles = Array.from(this.files.values())
    const completedFiles = allFiles.filter(f => f.status === 'completed' || f.status === 'failed')
    
    if (completedFiles.length === allFiles.length && allFiles.length > 0) {
      // 延迟一下确保UI更新
      setTimeout(() => {
        this.onComplete(allFiles)
      }, 100)
    }
  }

  /**
   * 获取总体进度
   * @returns {Object} 总体进度信息
   */
  getTotalProgress() {
    const allFiles = Array.from(this.files.values())
    if (allFiles.length === 0) return { progress: 0, completed: 0, total: 0 }

    const totalProgress = allFiles.reduce((sum, file) => sum + file.progress, 0)
    const completed = allFiles.filter(f => f.status === 'completed').length
    
    return {
      progress: Math.round(totalProgress / allFiles.length),
      completed,
      total: allFiles.length,
      uploading: allFiles.filter(f => f.status === 'uploading').length,
      failed: allFiles.filter(f => f.status === 'failed').length
    }
  }

  /**
   * 获取文件列表
   * @returns {Array} 文件列表
   */
  getFileList() {
    return Array.from(this.files.values())
  }

  /**
   * 获取文件数据
   * @param {string} fileId 文件ID
   * @returns {Object} 文件数据
   */
  getFile(fileId) {
    return this.files.get(fileId)
  }
} 